package com.xinlu.derek.common;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.CallableStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.hibernate.HibernateException;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateObjectRetrievalFailureException;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
import com.xinlu.common.exception.DBException;
import com.xinlu.util.tool.PageObject;

public class BaseDao extends HibernateDaoSupport {

	private static final Logger logger = LoggerFactory.getLogger(BaseDao.class);

	private JdbcTemplate jdbcTemplate;

	public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
		this.jdbcTemplate = jdbcTemplate;
	}

	/**
	 * 
	 * Function: load Object according to key
	 * 
	 * Produce describe: when occur ObjectNotFoundException,return null
	 * 
	 * @param clazz
	 * @param id
	 * @return
	 */
	protected Object load(Class clazz, Serializable id) {

		Object o = null;

		try {
			o = getHibernateTemplate().load(clazz, id);

			/* ************* 修改 ByAC ************** */
			if (judgeObjectDeleted(o))
				o = null;
			/* ************* 修改 ByAC ************** */

		} catch (HibernateObjectRetrievalFailureException e) {
			return null;
		}

		return o;
	}

	/**
	 * 
	 * function: 判断对象是否已删除
	 * 
	 * @param o
	 * @return
	 * 
	 * @author: AC Jun 24, 2010 9:26:38 AM
	 * 
	 */
	private boolean judgeObjectDeleted(Object o) {

		boolean isDeleted = false;

		if (o != null) {
			try {
				Method m = o.getClass().getMethod(BaseConstants.DELETE_UID_METHOD);

				if (m != null && m.invoke(o) != null) {
					isDeleted = true;
				}
			} catch (Exception e) {
				// e.printStackTrace();
			}
		}

		return isDeleted;
	}

	/**
	 * 
	 * Function: get Object according to key
	 * 
	 * Produce describe: when occur ObjectNotFoundException,return null
	 * 
	 * @param clazz
	 * @param id
	 * @return
	 */
	protected Object get(Class clazz, Serializable id) {

		Object o = null;

		try {
			o = getHibernateTemplate().get(clazz, id);
		} catch (HibernateObjectRetrievalFailureException e) {
			return null;
		}

		return o;
	}

	/**
	 * 
	 * function:find all Object
	 * 
	 * @param entityClass
	 * @return
	 * 
	 * @author Rain Stars Mar 11, 2009 7:08:05 PM
	 * @throws DBException
	 */
	protected List loadAll(Class entityClass) throws DBException {

		List list = null;

		try {
			list = getHibernateTemplate().loadAll(entityClass);

		} catch (Exception e) {
			throw new DBException(e.getMessage(), e);
		}
		return list;
	}

	/**
	 * 
	 * function:find all Object
	 * 
	 * @param entityClass
	 * @return
	 * 
	 * @author Rain Stars Mar 11, 2009 7:08:05 PM
	 * @throws DBException
	 */
	protected List loadAll(Class entityClass, Serializable[] ids) throws DBException {

		List list = null;

		if (ids == null) {
			throw new DBException("delete id must no null");
		}

		try {
			if (ids.length != 0) {

				list = new ArrayList();

				for (int i = 0; i < ids.length; i++) {

					Object o = this.load(entityClass, ids[i]);

					list.add(o);
				}
			}

		} catch (Exception e) {
			throw new DBException(e.getMessage(), e);
		}
		return list;
	}

	/**
	 * 
	 * Function: save Object
	 * 
	 * Produce describe:
	 * 
	 * @param obj
	 * @return
	 * @throws DBException
	 */
	protected Object save(Object obj) throws DBException {
		Object o = null;

		try {
			o = getHibernateTemplate().save(obj);
		} catch (Exception e) {
			throw new DBException(e.getMessage(), e);
		}

		return o;
	}

	/**
	 * 
	 * Function: update Object
	 * 
	 * Produce describe:
	 * 
	 * @param obj
	 * @throws DBException
	 */
	protected void update(Object obj) throws DBException {

		try {
			getHibernateTemplate().update(obj);
		} catch (Exception e) {

			throw new DBException(e.getMessage(), e);
		}

	}

	/**
	 * 
	 * function: save or update Object
	 * 
	 * @param entity
	 * @throws DBException
	 * 
	 * @author Rain Stars Mar 11, 2009 7:48:12 PM
	 */
	public void saveOrUpdate(final Object entity) throws DBException {

		try {
			getHibernateTemplate().saveOrUpdate(entity);

		} catch (Exception e) {
			throw new DBException(e.getMessage(), e);
		}
	}

	/**
	 * 
	 * function: save or update collection
	 * 
	 * @param entities
	 * @throws DBException
	 * 
	 * @author Rain Stars Mar 11, 2009 7:49:25 PM
	 */
	public void saveOrUpdateAll(final Collection entities) throws DBException {

		try {
			getHibernateTemplate().saveOrUpdateAll(entities);

		} catch (Exception e) {
			throw new DBException(e.getMessage(), e);
		}
	}

	/**
	 * 
	 * function:delete Object
	 * 
	 * @param entity
	 * @throws DBException
	 * 
	 * @author Rain Stars Mar 11, 2009 8:03:59 PM
	 */
	public void delete(Object entity) throws DBException {
		try {
			getHibernateTemplate().delete(entity);

		} catch (Exception e) {
			throw new DBException(e.getMessage(), e);
		}
	}

	/**
	 * 
	 * function: delete all Object
	 * 
	 * @param entities
	 * @throws DBException
	 * 
	 * @author Rain Stars Mar 11, 2009 8:02:04 PM
	 */
	public void deleteAll(final Collection entities) throws DBException {
		try {
			getHibernateTemplate().deleteAll(entities);

		} catch (Exception e) {
			throw new DBException(e.getMessage(), e);
		}
	}

	/**
	 * 
	 * function: delete Object by id
	 * 
	 * @param entityClass
	 * @throws DBException
	 * 
	 * @author Rain Stars Mar 11, 2009 8:02:04 PM
	 */
	public void delete(final Class entityClass, final Serializable id) throws DBException {
		if (id == null) {
			throw new DBException("delete id must no null");
		}
		this.deleteAll(entityClass, new Serializable[] { id });
	}

	/**
	 * 
	 * function: delete all Object by ids
	 * 
	 * @param entities
	 * @throws DBException
	 * 
	 * @author Rain Stars Mar 11, 2009 8:02:04 PM
	 */
	public void deleteAll(final Class entityClass, Serializable[] ids) throws DBException {
		if (ids == null) {
			throw new DBException("delete id must no null");
		}
		try {
			this.deleteAll(this.loadAll(entityClass, ids));

		} catch (Exception e) {
			throw new DBException(e.getMessage(), e);
		}
	}

	/**
	 * 
	 * Function: flush Session
	 * 
	 * Produce describe:
	 * 
	 * @throws DBException
	 * 
	 */
	protected void flush() throws DBException {
		try {
			getHibernateTemplate().flush();
		} catch (Exception e) {

			throw new DBException(e.getMessage(), e);
		}

	}

	/**
	 * Function: Execute a query with bind parameters.
	 * 
	 * Produce describe:
	 * 
	 * @param query
	 * @return
	 * @throws DBException
	 * @throws DBException
	 */
	protected List find(String hql) throws DBException {

		List list = null;

		try {
			list = getHibernateTemplate().find(hql);

		} catch (Exception e) {
			throw new DBException(e.getMessage(), e);
		}

		return list;
	}

	/**
	 * 
	 * Function: Execute a query with bind parameters.
	 * 
	 * Produce describe:
	 * 
	 * @param queryString
	 * @param value
	 * 
	 * @return
	 * @throws Exception
	 */
	protected List find(String hql, Object value) throws DBException {

		return find(hql, new Object[] { value });
	}

	/**
	 * Function: Execute a query with bind parameters.
	 * 
	 * Produce describe:
	 * 
	 * @param queryString
	 * @param values
	 * @param types
	 * @return
	 * @throws DBException
	 * @throws Exception
	 */
	protected List find(String hql, Object[] values) throws DBException {

		List list = null;

		/*
		 * Session session = getSession();
		 * 
		 * Query queryObject = null;
		 */

		/*
		 * if (values != null && types != null && values.length != types.length) {
		 * 
		 * throw new DBException( "Length of values array must match length of types array"); }
		 */

		/*
		 * if (session != null) {
		 * 
		 * try {
		 * 
		 * queryObject = session.createQuery(hql);
		 * 
		 * if (values != null) {
		 * 
		 * for (int i = 0; i < values.length; i++) {
		 * 
		 * if (types != null && types[i] != null) {
		 * 
		 * queryObject.setParameter(i, values[i], types[i]); } else {
		 * 
		 * queryObject.setParameter(i, values[i]); } } } } catch (Exception e) {
		 * 
		 * throw new DBException("exception.db.E00001"); } }
		 */
		try {
			// list= queryObject.list();
			list = super.getHibernateTemplate().find(hql, values);
		} catch (Exception e) {
			throw new DBException(e.getMessage(), e);
		}

		return list;

	}

	/**
	 * 
	 * Function: Execute Original Sql
	 * 
	 * Produce describe:
	 * 
	 * @param sql
	 * @param clazz
	 * @return
	 * @throws DBException
	 */
	protected List createSQLQuery(String sql, Class clazz) {

		Session session = getSession();

		SQLQuery sq = session.createSQLQuery(sql).addEntity("", clazz);

		List list = sq.list();

		return list;

	}

	/**
	 * 
	 * Function: Execute Original Sql
	 * 
	 * Produce describe:
	 * 
	 * @param sql
	 * @param clazz
	 * @return
	 * @throws DBException
	 */
	protected List createSQLQuery(final String sql) {

		List list = getHibernateTemplate().executeFind(new HibernateCallback() {

			public Object doInHibernate(Session session) throws HibernateException, SQLException {

				List list = null;

				SQLQuery sq = session.createSQLQuery(sql);

				return sq.list();
			}

		});

		return list;
	}

	/**
	 * 
	 * Function: Execute Original Sql
	 * 
	 * Produce describe:
	 * 
	 * @param sql
	 * @param clazz
	 * @return 传递参数的sql语句
	 * @throws DBException
	 */
	protected List createSQLQuery(final String sql, final Object[] obj) {

		List list = getHibernateTemplate().executeFind(new HibernateCallback() {

			public Object doInHibernate(Session session) throws HibernateException, SQLException {

				List list = null;

				SQLQuery sq = session.createSQLQuery(sql);

				if (obj != null) {

					for (int i = 0; i < obj.length; i++) {
						sq.setParameter(i, obj[i]);
					}
				}

				return sq.list();
			}

		});

		return list;
	}

	/**
	 * 通过存储过程获取分页对象.
	 * 
	 * @param <T>
	 * @param pageSize
	 * @param pageNo
	 * @param hql
	 * @param cl
	 *            告诉List容器装的单条记录对象
	 * @return
	 */
	protected <T> PageObject findPageObjectByProc(final int pageSize, final int pageNo, final String hql, final Class<T> cl) {

		return (PageObject) getHibernateTemplate().execute(new HibernateCallback() {

			@Override
			public Object doInHibernate(Session session) throws HibernateException, SQLException {

				PageObject po = new PageObject();

				CallableStatement cs = session.connection().prepareCall("{call spPageObject(?,?,?,?,?)}");

				cs.setString(1, hql);
				cs.setInt(2, pageNo);
				cs.setInt(3, pageSize);
				cs.registerOutParameter(4, java.sql.Types.INTEGER);
				cs.registerOutParameter(5, java.sql.Types.INTEGER);

				List<T> lst = new ArrayList<T>();
				Method[] methods = cl.getMethods();

				cs.executeQuery();
				boolean needSearchSecond = true;
				ResultSet rsFrist = cs.getResultSet(); // 过滤第一个结果集
				// 获取结果集的列集合
				ResultSetMetaData rmd = null;
				if (rsFrist.next()) {
					needSearchSecond = false;
					rmd = rsFrist.getMetaData();
					try {
						lst.add(getPo(cl, rmd, methods, rsFrist));
					} catch (InstantiationException e) {
						e.printStackTrace();
					} catch (IllegalAccessException e) {
						e.printStackTrace();
					}
				}

				ResultSet rs = null;
				if (needSearchSecond) {
					if (cs.getMoreResults()) {
						rs = cs.getResultSet();
						rmd = cs.getMetaData();
					}
				} else {
					rs = rsFrist;
				}

				if (rs != null) {
					while (rs.next()) {
						try {
							lst.add(getPo(cl, rmd, methods, rs));
						} catch (InstantiationException e) {
							e.printStackTrace();
						} catch (IllegalAccessException e) {
							e.printStackTrace();
						}
					}
				}

				po.setItems(lst);
				po.setRecordCount(cs.getInt(5));
				po.setPageSize(pageSize);

				int startIndex = (pageNo - 1) * pageSize;
				if (startIndex < 0) {
					startIndex = 0;
				}
				po.setStartIndex(startIndex);

				return po;
			}
		});
	}

	/**
	 * 获取实体
	 * @param <T>
	 * @param cl
	 * @param rmd
	 * @param methods
	 * @param rs
	 * @return
	 * @throws SQLException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 */
	private <T> T getPo(Class<T> cl, ResultSetMetaData rmd, Method[] methods, ResultSet rs) throws SQLException, InstantiationException, IllegalAccessException {
		// 获取类的实例
		T t = cl.newInstance();
		try {

			// 遍历列，然后添加到T实例
			int colCount = rmd.getColumnCount();
			for (int i = 1; i <= colCount; i++) {

				String colName = rmd.getColumnName(i).toLowerCase().replace("_", "");

				try {
					for (Method mth : methods) {
						if (("set" + colName).equals(mth.getName().toLowerCase())) {
							try {
								Object value = rs.getObject(i);

								if (value instanceof BigDecimal) {
									value = ((BigDecimal) value).doubleValue();
								} else if (value instanceof BigInteger) {
									value = ((BigInteger) value).intValue();
								}

								mth.invoke(t, value);
								break;
							} catch (IllegalArgumentException e) {
								// 对象类型不同
								e.printStackTrace();
								break;
							} catch (InvocationTargetException e) {
								e.printStackTrace();
								break;
							}
						}
					}
				} catch (SecurityException e) {
					e.printStackTrace();
				}
			}

		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		
		return t;
	}

	/* =========================== 新增方法 开始 =========================== */

	/**
	 * 
	 * function: 清理一级缓存, 防止造成内存溢出
	 * 
	 * 
	 * @author: AC Jul 2, 2010 1:17:25 AM
	 * 
	 */
	public void clearLevel1Cache() {

		try {
			getSession().flush();
			getSession().clear();
		} catch (Exception e) {

			throw new DBException(e.getMessage(), e);
		}
	}

	/**
	 * function:使用原生JDBC查询
	 * 
	 * @param sql
	 * @param args
	 * 
	 * author:DBA Aug 26, 2010 3:45:00 PM
	 */
	public List query(String sql, Object[] args) {

		return jdbcTemplate.queryForList(sql, args);
	}

	/**
	 * 使用原生JDBC查询
	 * 
	 * @param sql
	 * @return
	 */
	public List query(String sql) {
		return jdbcTemplate.queryForList(sql);
	}

	// ///////////////////////////////////////////////////////////////////////
	// Add By Derek on 2011-05-15
	// ///////////////////////////////////////////////////////////////////////
	String callName = null;
	Object[] objs = null;

	protected void saveByProc(String callName1) {

		this.callName = callName1;

		getHibernateTemplate().execute(new HibernateCallback() {

			@Override
			public Object doInHibernate(Session session) throws HibernateException, SQLException {

				CallableStatement cs = session.connection().prepareCall(callName);

				cs.execute();

				return null;
			}
		});
	}

	protected void saveByProc(String callName1, Object[] objsVal) {

		this.callName = callName1;
		this.objs = objsVal;

		getHibernateTemplate().execute(new HibernateCallback() {

			@Override
			public Object doInHibernate(Session session) throws HibernateException, SQLException {

				CallableStatement cs = session.connection().prepareCall(callName);

				for (int i = 0; i < objs.length; i++) {

					cs.setObject(i + 1, objs[i]);
				}

				cs.executeUpdate();

				return null;
			}
		});
	}

	/* =========================== 新增方法 结束 =========================== */

}
